/*
 * Created on Dec 18, 2007
 */
package com.mgmaps.utils;

import java.io.InputStream;
import javax.microedition.lcdui.*;
import com.mgmaps.app.APPCommands;
import com.mgmaps.app.APPMidlet;
import com.mgmaps.tools.StringTools;

/**
 * Interface for {@link com.mgmaps.utils.FileUtils} and other file API classes.
 * Used for fooling the class verifier. Also includes most of the common processing to
 * read files through a file connection and to show the filesystem browser. 
 */
public abstract class IFileUtils implements CommandListener, Runnable {
  // file filter types
  public static final int FFTYPE_NAME = 0; // match name
  public static final int FFTYPE_STARTSWITH = 1; // match name (starts with)
  public static final int FFTYPE_ENDSWITH = 2; // match name (ends with)
  public static final int FFTYPE_DIRCONTAINS = 3; // match directory contents
  
  // public static final int FILETYPE_FILE = 1;
  // public static final int FILETYPE_DIR = 2;
  // public static final int FILETYPE_ROOT = 4;
  
  protected List fileList;

  protected TextField textField;
  protected String fileFilter;
  protected String currentDir;
  protected String oldCurrentDir;
  protected int fileFilterType;
  protected boolean found;
  protected Displayable prevDisp;

  /**
   * Initialize the file list for the file browser.
   */
  public void initFileList() {
    fileList = new List("", List.IMPLICIT);
    fileList.setSelectCommand(APPCommands.instance.selectCommand);
    fileList.addCommand(APPCommands.instance.cancelCommand);
    fileList.setCommandListener(this);
  }
  
  /**
   * Check if a filename starts or ends with any of the names included in a filter.
   * @param s string to check
   * @param filter filter
   * @param ends false if checking for "starts", true if "ends"
   * @return true if found, false otherwise
   */
  protected boolean checkStartsEnds(String s, String filter, boolean ends) {
    String[] splits = StringTools.split(filter, '|');
    for (int i=0; i<splits.length; i++)
      if ((!ends && s.startsWith(splits[i])) || 
          (ends && s.endsWith(splits[i])))
        return true;
    
    return false;
  }
  
  /**
   * Populate and show the list.
   */
  protected abstract void populateList() throws Exception;
  
  /**
   * Show the FileBrowser.
   *
   * @param tf text field to be populated
   * @param ff filter for files returned (filename expected)
   * @param cd starting directory (null = root)
   */
  public void setShowArgs(TextField tf, int fftype, String ff, String cd, Displayable pd) {
    textField = tf;
    fileFilter = ff;
    currentDir = cd;
    fileFilterType = fftype;
    found = false;
    prevDisp = pd;
  }
  
  /**
   * Called when a file was found.
   * 
   * @param filename name of the file
   */
  protected void foundFile(String filename) {
    textField.setString(filename);
    Display.getDisplay(APPMidlet.instance).setCurrent(prevDisp);
    found = true;
  }
  
  /**
   * Main thread method.
   * 
   * @see java.lang.Runnable#run()
   */
  public void run() {
    try {
      populateList();
      if (found)
        return;
      
      if (fileList.size() > 0)
        fileList.setSelectedIndex(0, true);
        
      oldCurrentDir = currentDir;
    }
    catch (Exception ex) {
      ExtLogger.handle(ex, false, "Error reading filesystem");
      currentDir = oldCurrentDir;
    }

    fileList.setTitle("/" + (currentDir == null ? "" : currentDir));
    
    // show the list
    Display.getDisplay(APPMidlet.instance).setCurrent(fileList);
  }

  /**
   * Called when a command is selected.
   * @param cmd command
   * @param disp displayable (ignored
   * @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command, javax.microedition.lcdui.Displayable)
   */
  public void commandAction(Command cmd, Displayable disp) {
    // cancel
    if (cmd == APPCommands.instance.cancelCommand)
      Display.getDisplay(APPMidlet.instance).setCurrent(prevDisp);
    
    // select
    else if (cmd == List.SELECT_COMMAND || cmd == APPCommands.instance.selectCommand) {
      // check what we have selected
      int index = fileList.getSelectedIndex();
      String filename = fileList.getString(index);
      // error
      if (filename == null)
        return;

      String checkFilename = filename;
      boolean isDir = filename.endsWith("/");
      if (isDir)
        checkFilename = filename.substring(0, filename.length()-1);
      
      // found
      if (fileFilterType == FFTYPE_NAME && checkFilename.equals(fileFilter)
       || fileFilterType == FFTYPE_STARTSWITH && checkStartsEnds(checkFilename, fileFilter, false)
       || fileFilterType == FFTYPE_ENDSWITH && checkStartsEnds(checkFilename, fileFilter, true))
        foundFile(currentDir + checkFilename);
      
      // directory, dive in
      else if (isDir) {
        // parent dir
        if (checkFilename.equals("..")) {
          int len = currentDir.length();
          int lastpos;
          // get the 'but-last' slash
          for (lastpos=len-2; lastpos>=0; lastpos--)
            if (currentDir.charAt(lastpos) == '/')
              break;
          if (lastpos < 0)
            currentDir = null;
          else
            currentDir = currentDir.substring(0, lastpos+1);
        }
        // we're in root
        else if (currentDir == null || currentDir.equals("/"))
          currentDir = filename;
        else
          currentDir += filename;
        
        // trim slashes
        if (currentDir != null) {
          while (currentDir.startsWith("/"))
            currentDir = currentDir.substring(1);
          while (currentDir.endsWith("//"))
            currentDir = currentDir.substring(0, currentDir.length()-1);
        }
          
        AsyncCalls.instance.setRunnable(this);
      }
    }
  }

  /**
   * Check if a directory exists.
   *
   * @param filename name of the directory
   * @return true if the file exists and is a directory, false otherwise
   * @throws Exception if an exception occurs
   */
  // public abstract boolean dirExists(String filename) throws Exception;
  
  /**
   * Read a file.
   * 
   * @param filename file name
   * @return file data
   * @throws Exception if an exception occurs
   */
  public abstract byte[] readFile(String filename) throws Exception;
  
  /**
   * Open file input stream.
   * 
   * @param filename file name
   * @return input stream
   * @throws Exception if an exception occurs
   */
  public abstract InputStream openFileInputStream(String filename) throws Exception;
}
